// Copyright (C) 2002-2007 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#include "IrrCompileConfig.h"

#ifdef _IRR_SUPPORT_OLO

#include "CIrrDeviceOlo.h"
#include "IEventReceiver.h"
#include "irrList.h"
#include "os.h"

#include "CTimer.h"
#include "irrString.h"
#include "COSOperator.h"
#include "dimension2d.h"
//#include <winuser.h>
#include "irrlicht.h"

#ifdef _IRR_COMPILE_WITH_3DIJOY_CTL_
#include "EventSender_3dijoy_Linux.h"
EventSender_3dijoy_Linux sender3dijoy;
#endif

namespace irr
{
	namespace video
	{
		#ifdef _IRR_SUPPORT_OLO
		IVideoDriver* createOpenGLESDriver(const core::dimension2d<s32>& screenSize,
                 bool fullscreen,
								 bool stencilBuffer, io::IFileSystem* io, bool vsync, bool antiAlias);
		#endif
	}
} // end namespace irr



// ...

namespace irr
{

//! constructor
CIrrDeviceOlo::CIrrDeviceOlo(video::E_DRIVER_TYPE driverType,
				 core::dimension2d<s32> windowSize,
				 u32 bits, bool fullscreen,
				 bool stencilbuffer, bool vsync,
				 bool antiAlias,
				 bool highPrecisionFPU,
				 IEventReceiver* receiver,
				 const char* version)
: CIrrDeviceStub(version, receiver), ChangedToFullScreen(false),
	FullScreen(fullscreen), IsNonNTWindows(false), Resized(false), Close(false)
{
	core::stringc winversion;
	//getWindowsVersion(winversion);
	Operator = new COSOperator(winversion.c_str());
	os::Printer::log(winversion.c_str(), ELL_INFORMATION);

	// create window

	//HINSTANCE hInstance = GetModuleHandle(0);

	#ifdef _DEBUG
	setDebugName("CIrrDeviceOlo");
	#endif

#ifdef _IRR_COMPILE_WITH_3DIJOY_CTL_
  sender3dijoy.Init(this);
#endif

	// create driver

	createDriver(driverType, windowSize, bits, fullscreen, stencilbuffer, vsync, antiAlias, highPrecisionFPU);

	if (VideoDriver)
		createGUIAndScene();

	// register environment
	// ...
}



//! destructor
CIrrDeviceOlo::~CIrrDeviceOlo()
{
	// unregister environment
#ifdef _IRR_COMPILE_WITH_3DIJOY_CTL_
  sender3dijoy.Term();
#endif

}



//! create the driver
void CIrrDeviceOlo::createDriver(video::E_DRIVER_TYPE driverType,
				   const core::dimension2d<s32>& windowSize,
				   u32 bits,
				   bool fullscreen,
				   bool stencilbuffer,
				   bool vsync,
				   bool antiAlias,
				   bool highPrecisionFPU)
{
	switch(driverType)
	{
	case video::EDT_OPENGLES:
		#ifdef _IRR_COMPILE_WITH_OPENGL_ES_
		if (fullscreen)	switchToFullScreen(windowSize.Width, windowSize.Height, bits);
		VideoDriver = video::createOpenGLESDriver(windowSize, fullscreen, stencilbuffer, FileSystem,
			vsync, antiAlias);
		if (!VideoDriver)
		{
			os::Printer::log("Could not create OpenGL|ES driver.", ELL_ERROR);
		}
		#else
		os::Printer::log("OpenGL|ES driver was not compiled in.", ELL_ERROR);
		#endif
		break;

	case video::EDT_SOFTWARE:

		#ifdef _IRR_COMPILE_WITH_SOFTWARE_
		if (fullscreen)	switchToFullScreen(windowSize.Width, windowSize.Height, bits);
		VideoDriver = video::createSoftwareDriver(windowSize, fullscreen, FileSystem, this);
		#else
		os::Printer::log("Software driver was not compiled in.", ELL_ERROR);
		#endif

		break;

	case video::EDT_BURNINGSVIDEO:
		#ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
		if (fullscreen)	switchToFullScreen(windowSize.Width, windowSize.Height, bits);
		VideoDriver = video::createSoftwareDriver2(windowSize, fullscreen, FileSystem, this);
		#else
		os::Printer::log("Burning's Video driver was not compiled in.", ELL_ERROR);
		#endif
		break;

	case video::EDT_NULL:
		// create null driver
		VideoDriver = video::createNullDriver(FileSystem, windowSize);
		break;

	default:
		os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
		break;
	}
}



//! runs the device. Returns false if device wants to be deleted
bool CIrrDeviceOlo::run()
{
	os::Timer::tick();

	//MSG msg;
#ifdef _IRR_COMPILE_WITH_3DIJOY_CTL_
  sender3dijoy.PostEvent2Irr();
#endif

	bool quit = false;
/*
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);

		if (ExternalWindow && msg.hwnd == HWnd)
			WndProc(HWnd, msg.message, msg.wParam, msg.lParam);
		else
			DispatchMessage(&msg);

		if (msg.message == WM_QUIT) // HOW TO QUIT ???
			quit = true;
	}
*/

	if (!quit)
		resizeIfNecessary();

	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
	return !quit;
}


//! Pause the current process for the minimum time allowed only to allow other processes to execute
void CIrrDeviceOlo::yield()
{ // Sleep(1);
}

//! Pause execution and let other processes to run for a specified amount of time.
void CIrrDeviceOlo::sleep(u32 timeMs, bool pauseTimer)
{
	bool wasStopped = Timer ? Timer->isStopped() : true;

	struct timespec ts;
	ts.tv_sec = (time_t) (timeMs / 1000);
	ts.tv_nsec = (long) (timeMs % 1000) * 1000000;

	if (pauseTimer && !wasStopped)
		Timer->stop();

	//nanosleep(&ts, NULL);

	if (pauseTimer && !wasStopped)
		Timer->start();
}


void CIrrDeviceOlo::resizeIfNecessary()
{
	if (!Resized)
		return;
/*
	RECT r;
	GetClientRect(HWnd, &r);

	char tmp[255];

	if (r.right < 2 || r.bottom < 2)
	{
		sprintf(tmp, "Ignoring resize operation to (%ld %ld)", r.right, r.bottom);
		os::Printer::log(tmp);
	}
	else
	{
		sprintf(tmp, "Resizing window (%ld %ld)", r.right, r.bottom);
		os::Printer::log(tmp);

		getVideoDriver()->OnResize(irr::core::dimension2d<irr::s32>(r.right, r.bottom));
	}
*/
	Resized = false;
}



//! sets the caption of the window
void CIrrDeviceOlo::setWindowCaption(const wchar_t* text)
{
  /*
	if (IsNonNTWindows)
	{
		core::stringc s = text;
		SetWindowTextA(HWnd, s.c_str());
	}
	else
		SetWindowTextW(HWnd, text);
  */
}



//! presents a surface in the client area
void CIrrDeviceOlo::present(video::IImage* image, s32 windowId, core::rect<s32>* src )
{
/*
	HWND hwnd = HWnd;
	if ( windowId )
		hwnd = (HWND)windowId;

	HDC dc = GetDC(hwnd);

	if ( dc )
	{
		RECT rect;
		GetClientRect(hwnd, &rect);
		const void* memory = (const void *)image->lock();

		BITMAPV4HEADER bi;
		ZeroMemory (&bi, sizeof(bi));
		bi.bV4Size          = sizeof(BITMAPINFOHEADER);
		bi.bV4BitCount      = image->getBitsPerPixel();
		bi.bV4Planes        = 1;
		bi.bV4Width         = image->getDimension().Width;
		bi.bV4Height        = -image->getDimension().Height;
		bi.bV4V4Compression = BI_BITFIELDS;
		bi.bV4AlphaMask     = image->getAlphaMask ();
		bi.bV4RedMask       = image->getRedMask ();
		bi.bV4GreenMask     = image->getGreenMask();
		bi.bV4BlueMask      = image->getBlueMask();

		if ( src )
		{
			StretchDIBits(dc, 0,0, rect.right, rect.bottom,
					src->UpperLeftCorner.X, src->UpperLeftCorner.Y,
					src->getWidth(), src->getHeight(),
					memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY);
		}
		else
		{
			StretchDIBits(dc, 0,0, rect.right, rect.bottom,
					0, 0, image->getDimension().Width, image->getDimension().Height,
					memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY);
		}

		image->unlock();

		ReleaseDC(hwnd, dc);
	}
*/
}



//! notifies the device that it should close itself
void CIrrDeviceOlo::closeDevice()
{
  Close = true;
  /*
	MSG msg;
	PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
	PostQuitMessage(0);
	PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE);
	DestroyWindow(HWnd);
	*/
}



//! returns if window is active. if not, nothing need to be drawn
bool CIrrDeviceOlo::isWindowActive() const
{
//	bool ret = (GetActiveWindow() == HWnd);
//	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
//	return ret;
  return true;
}



//! switches to fullscreen
bool CIrrDeviceOlo::switchToFullScreen(s32 width, s32 height, s32 bits)
{
/*
	DEVMODE dm;
	memset(&dm, 0, sizeof(dm));
	dm.dmSize = sizeof(dm);
	// use default values from current setting
	EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
	dm.dmPelsWidth = width;
	dm.dmPelsHeight	= height;
	dm.dmBitsPerPel	= bits;
	dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;

	LONG ret = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
	if (ret != DISP_CHANGE_SUCCESSFUL)
	{ // try again without forcing display frequency
		dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
		ret = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
	}

	switch(ret)
	{
	case DISP_CHANGE_SUCCESSFUL:
		ChangedToFullScreen = true;
		return true;
	case DISP_CHANGE_RESTART:
		os::Printer::log("Switch to fullscreen: The computer must be restarted in order for the graphics mode to work.", ELL_ERROR);
		return false;
	case DISP_CHANGE_BADFLAGS:
		os::Printer::log("Switch to fullscreen: An invalid set of flags was passed in.", ELL_ERROR);
		return false;
	case DISP_CHANGE_BADPARAM:
		os::Printer::log("Switch to fullscreen: An invalid parameter was passed in. This can include an invalid flag or combination of flags.", ELL_ERROR);
		return false;
	case DISP_CHANGE_FAILED:
		os::Printer::log("Switch to fullscreen: The display driver failed the specified graphics mode.", ELL_ERROR);
		return false;
	case DISP_CHANGE_BADMODE:
		os::Printer::log("Switch to fullscreen: The graphics mode is not supported.", ELL_ERROR);
		return false;
	}

	os::Printer::log("An unknown error occured while changing to fullscreen.", ELL_ERROR);
*/
	return false;
}


/*
//! returns the win32 cursor control
CIrrDeviceOlo::CCursorControl* CIrrDeviceOlo::getWin32CursorControl()
{
	//return Win32CursorControl;
	return 0;
}
*/

//! \return Returns a pointer to a list with all video modes supported
//! by the gfx adapter.
video::IVideoModeList* CIrrDeviceOlo::getVideoModeList()
{
/*
	if (!VideoModeList.getVideoModeCount())
	{
		// enumerate video modes.
		DWORD i=0;
		DEVMODE mode;
		memset(&mode, 0, sizeof(mode));
		mode.dmSize = sizeof(mode);

		while (EnumDisplaySettings(NULL, i, &mode))
		{
			VideoModeList.addMode(core::dimension2d<s32>(mode.dmPelsWidth, mode.dmPelsHeight),
				mode.dmBitsPerPel);

			++i;
		}

		if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &mode))
			VideoModeList.setDesktop(mode.dmBitsPerPel, core::dimension2d<s32>(mode.dmPelsWidth, mode.dmPelsHeight));
	}

	return &VideoModeList;
*/
  return 0;
}


void CIrrDeviceOlo::getWindowsVersion(core::stringc& out)
{
/*
	OSVERSIONINFOEX osvi;
	BOOL bOsVersionInfoEx;

	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

	if(!(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO*) &osvi)))
	{
		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
			return;
	}

	switch (osvi.dwPlatformId)
	{
	case VER_PLATFORM_WIN32_NT:
		if (osvi.dwMajorVersion <= 4)
			out.append("Microsoft Windows NT ");
		if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
			out.append("Microsoft Windows 2000 ");
		if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
			out.append("Microsoft Windows XP ");
		if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0 )
			out.append("Microsoft Windows Vista ");

		if( bOsVersionInfoEx )
		{
			#ifdef VER_SUITE_ENTERPRISE
			if (osvi.wProductType == VER_NT_WORKSTATION)
			{
#ifndef __BORLANDC__
				if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
					out.append("Personal ");
				else
					out.append("Professional ");
#endif
			}
			else if (osvi.wProductType == VER_NT_SERVER)
			{
				if( osvi.wSuiteMask & VER_SUITE_DATACENTER )
					out.append("DataCenter Server ");
				else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
					out.append("Advanced Server ");
				else
					out.append("Server ");
			}
			#endif
		}
		else
		{
			HKEY hKey;
			char szProductType[80];
			DWORD dwBufLen;

			RegOpenKeyEx( HKEY_LOCAL_MACHINE,
				"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
				0, KEY_QUERY_VALUE, &hKey );
			RegQueryValueEx( hKey, "ProductType", NULL, NULL,
				(LPBYTE) szProductType, &dwBufLen);
			RegCloseKey( hKey );

			if (lstrcmpi( "WINNT", szProductType) == 0 )
				out.append("Professional ");
			if ( lstrcmpi( "LANMANNT", szProductType) == 0 )
				out.append("Server " );
			if ( lstrcmpi( "SERVERNT", szProductType) == 0 )
				out.append("Advanced Server ");
		}

		// Display version, service pack (if any), and build number.

		char tmp[255];

		if (osvi.dwMajorVersion <= 4 )
		{
			sprintf (tmp, "version %ld.%ld %s (Build %ld)",
				osvi.dwMajorVersion,
				osvi.dwMinorVersion,
				osvi.szCSDVersion,
				osvi.dwBuildNumber & 0xFFFF);
		}
		else
		{
			sprintf (tmp, "%s (Build %ld)", osvi.szCSDVersion,
				osvi.dwBuildNumber & 0xFFFF);
		}

		out.append(tmp);
		break;

	case VER_PLATFORM_WIN32_WINDOWS:

		IsNonNTWindows = true;

		if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
		{
			out.append("Microsoft Windows 95 ");
			if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
				out.append("OSR2 " );
		}

		if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
		{
			out.append("Microsoft Windows 98 ");
			if ( osvi.szCSDVersion[1] == 'A' )
				out.append( "SE " );
		}

		if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
			out.append("Microsoft Windows Me ");

		break;

	case VER_PLATFORM_WIN32s:

		IsNonNTWindows = true;
		out.append("Microsoft Win32s ");
		break;
	}
*/
}

//! Notifies the device, that it has been resized
void CIrrDeviceOlo::OnResized()
{
	Resized = true;
}

//! Sets if the window should be resizeable in windowed mode.
void CIrrDeviceOlo::setResizeAble(bool resize)
{

}


IRRLICHT_API IrrlichtDevice* IRRCALLCONV createDeviceEx(
	const SIrrlichtCreationParameters& parameters)
{
	CIrrDeviceOlo* dev = new CIrrDeviceOlo(
		parameters.DriverType,
		parameters.WindowSize,
		parameters.Bits,
		parameters.Fullscreen,
		parameters.Stencilbuffer,
		parameters.Vsync,
		parameters.AntiAlias,
		parameters.HighPrecisionFPU,
		parameters.EventReceiver,
		parameters.SDK_version_do_not_use);

	if (dev && !dev->getVideoDriver() && parameters.DriverType != video::EDT_NULL)
	{
		dev->closeDevice(); // destroy window
		dev->run(); // consume quit message
		dev->drop();
		dev = 0;
	}

	return dev;
}


} // end namespace

#endif // _IRR_SUPPORT_OLO
